package com.bulls.dsp.service.impl;

import com.bulls.dsp.common.Constant;
import com.bulls.dsp.common.PageInfo;
import com.bulls.dsp.common.util.QiniuHelper;
import com.bulls.dsp.mapper.AdResourceMapper;
import com.bulls.dsp.mapper.AdvertMapper;
import com.bulls.dsp.mapper.OrderMapper;
import com.bulls.dsp.bean.FileInfo;
import com.bulls.dsp.exception.DuplicateException;
import com.bulls.dsp.exception.FileUploadIOException;
import com.bulls.dsp.exception.NoSuchOrderException;
import com.bulls.dsp.bean.enums.AdStatus;
import com.bulls.dsp.bean.enums.FeedFrom;
import com.bulls.dsp.bean.enums.FeedStyle;
import com.bulls.dsp.bean.enums.MonitorType;
import com.bulls.dsp.bean.mybatis.AdResource;
import com.bulls.dsp.bean.mybatis.Advert;
import com.bulls.dsp.bean.mybatis.Order;
import com.bulls.dsp.service.IAdService;
import com.qiniu.api.auth.AuthException;
import com.qiniu.api.io.PutRet;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

@Service
@Transactional
public class AdServiceImpl implements IAdService {


    @Resource
    private AdvertMapper advertMapper;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private AdResourceMapper adResourceMapper;

    @Override
    public void insertOrUpdate(Advert advert) {
        this.checkNameDuplicate(advert);
        this.convertEnumsValue2Code(advert);

        if (advert.getId() == null) {
            advertMapper.insert(advert);
        } else {
            advertMapper.update(advert);
        }

        this.insertOrUpdateRes(advert);
    }

    private void insertOrUpdateRes(Advert advert) {
        Optional.ofNullable(advert.getFileInfos()).ifPresent(val -> {

            List<AdResource> olds = adResourceMapper.getByAdId(advert.getId());

            for (AdResource res : olds) {
                boolean isExist = false;
                for (FileInfo fileInfo : val) {
                    if (fileInfo.getName().equals(res.getResKey())) {
                        isExist = true;
                        break;
                    }
                }
                if (!isExist) {
                    adResourceMapper.delete(res.getId());
                }
            }

            for (FileInfo fileInfo : val) {
                AdResource resource = adResourceMapper.getByResKey(fileInfo.getName());
                if (resource != null) {
                    resource.setAdId(advert.getId());
                    try {
                        adResourceMapper.fillAdId(resource);
                    } catch (DataIntegrityViolationException ignore) {
                    }
                }
            }


        });

    }

    private void convertEnumsValue2Code(Advert advert) {
        advert.setOrderId(
                Optional.ofNullable(advert.getOrderName())
                        .map(name -> orderMapper.getOrderByName(name))
                        .map(Order::getId)
                        .orElseThrow(NoSuchOrderException::new)
        );

        advert.setAdStatus(
                Optional.ofNullable(advert.getAdStatus())
                        .map(AdStatus::getByStringValue)
                        .map(AdStatus::getValue)
                        .map(String::valueOf)
                        .orElse(null)
        );

        advert.setFeedStyle(
                Optional.ofNullable(advert.getFeedStyle())
                        .map(FeedStyle::getByStringValue)
                        .map(FeedStyle::getValue)
                        .map(String::valueOf)
                        .orElse(null)

        );

        advert.setMonitorType(
                Optional.ofNullable(advert.getMonitorType())
                        .map(MonitorType::getByStringValue)
                        .map(MonitorType::getValue)
                        .map(String::valueOf)
                        .orElse(null)

        );

        advert.setStartDate(
                Optional.ofNullable(advert.getDate())
                        .map(dates -> dates.get(0))
                        .orElse(null)
        );

        advert.setEndDate(
                Optional.ofNullable(advert.getDate())
                        .map(dates -> dates.get(1))
                        .orElse(null)
        );
    }

    private void checkNameDuplicate(Advert advert) {

        Advert old = advertMapper.getAdvertByName(advert.getName());
        if (!(old == null || Objects.equals(old.getId(), advert.getId()))) {
            throw new DuplicateException("广告名称重复,请重试");
        }

    }

    @Override
    @Transactional(readOnly = true)
    public PageInfo<?> findAllByIfNotNull(String name, int page, int size) {

        name = StringUtils.isEmpty(name) ? name : "%" + name.trim() + "%";
        List<Advert> adverts = advertMapper.getAdverts(name, page, size);
        this.convertEnumsCode2Value(adverts);

        return PageInfo.builder()
                .totalElements(advertMapper.getAdvertsCount(name))
                .number(page)
                .size(size)
                .content(adverts)
                .build();

    }

    private void convertEnumsCode2Value(List<Advert> adverts) {
        adverts.forEach(advert -> {
            List<Date> date = new ArrayList<>();
            Optional.ofNullable(advert.getStartDate()).map(date::add);
            Optional.ofNullable(advert.getEndDate()).map(date::add);

            advert.setDate(date);

            advert.setAdStatus(
                    Optional.ofNullable(advert.getAdStatus())
                            .map(AdStatus::getByStringCode)
                            .map(AdStatus::getStatus)
                            .orElse(Constant.UNKNOWN)
            );

            advert.setMonitorType(
                    Optional.ofNullable(advert.getMonitorType())
                            .map(MonitorType::getByStringCode)
                            .map(MonitorType::getType)
                            .orElse(Constant.UNKNOWN)
            );

            advert.setFeedStyle(
                    Optional.ofNullable(advert.getFeedStyle())
                            .map(FeedStyle::getByStringCode)
                            .map(FeedStyle::getType)
                            .orElse(Constant.UNKNOWN)
            );

            advert.setFileInfos(
                    Optional.ofNullable(advert.getId())
                            .map(this::getFileInfos)
                            .orElse(null)
            );

        });
    }

    @Override
    public Map<String, List<List<String>>> getEnumsList() {

        Map<String, List<List<String>>> result = new HashMap<>();
        result.put("adStatus", AdStatus.getCodeValueList());
        result.put("feedFrom", FeedFrom.getCodeValueList());
        result.put("feedStyle", FeedStyle.getCodeValueList());
        result.put("monitorType", MonitorType.getCodeValueList());

        return result;
    }

    @Override
    public void changeAdStatus(Long id, String statusTo) {

        Advert advert = new Advert();
        advert.setId(id);
        advert.setAdStatus(statusTo);
        advertMapper.updateStatus(advert);
    }

    @Override
    public void upload(MultipartFile multipartFile) throws AuthException {
        if (!multipartFile.isEmpty()) {
            try {
                // 这里只是简单例子，文件直接输出到项目路径下。
                // 实际项目中，文件需要输出到指定位置，需要在增加代码处理。
                // 还有关于文件格式限制、文件大小限制，详见：中配置。
                String fileName = multipartFile.getOriginalFilename();
                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(fileName)));
                out.write(multipartFile.getBytes());
                out.flush();
                out.close();
                PutRet ret = QiniuHelper.upload(fileName);

                AdResource adResource = new AdResource();
                adResource.setResKey(ret.getKey());

                adResourceMapper.insert(adResource);

            } catch (IOException e) {
                throw new FileUploadIOException();
            }
        }
    }

    private List<FileInfo> getFileInfos(Long adId) {
        List<FileInfo> result = new ArrayList<>();
        List<AdResource> resources = adResourceMapper.getByAdId(adId);
        resources.forEach(adResource -> {
            FileInfo info = new FileInfo();
            info.setName(adResource.getResKey());
            info.setUid(adResource.getId().toString());
            info.setStatus("done");
            info.setUrl(Constant.QINIU_URL + adResource.getResKey());
            result.add(info);
        });
        return result;
    }
}
